#include <gic-v2.h>
#include "io.h"
#include <stdint.h>

#define ARM_GIC_MAX_NR 1
static struct gic_chip_data gic_data[ARM_GIC_MAX_NR];

#define gic_dist_base(d) ((d)->raw_dist_base)
#define gic_cpu_base(d) ((d)->raw_cpu_base)


static unsigned int gic_get_cpumask(struct gic_chip_data *gic){
    unsigned long base = gic_dist_base(gic);
    unsigned int mask, i;

    for(i = mask = 0;i < 32; i += 4){
        mask = readl(base + GIC_DIST_TARGET + i);
        mask |= mask >> 16;
        mask |= mask >> 8;
        if(mask)
            break;
    }

    return mask;
}

static unsigned long gic_get_dist_base(void){
    struct gic_chip_data *gic = &gic_data[0];

    return gic_dist_base(gic);
}

static void gic_set_irq(int irq, unsigned int offset){
    unsigned int mask = 1 << (irq % 32);

	writel(mask, gic_get_dist_base() + offset + (irq / 32) * 4);
}

void gicv2_unmask_irq(int irq){
    gic_set_irq(irq, GIC_DIST_ENABLE_SET);
}

static void gic_dist_init(struct gic_chip_data *gic){
    unsigned long base = gic_dist_base(gic);
    unsigned int cpumask;
    unsigned int gic_irqs = gic->gic_irqs;
    int i;

    writel(GICD_DISABLE, base + GIC_DIST_CTRL);

    cpumask = gic_get_cpumask(gic);
    cpumask |= cpumask << 8;
    cpumask |= cpumask << 16;

    for(i=32; i < gic_irqs; i += 4)
        writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
    
    for(i = 0; i < gic_irqs; i += 32){
        writel(GICD_INT_EN_CLR_X32, base + 
                GIC_DIST_ACTIVE_CLEAR + i / 8);
        writel(GICD_INT_EN_CLR_X32, base + 
                GIC_DIST_ENABLE_CLEAR + i / 8);
    }


	/*打开SGI中断（0～15），可能SMP会用到*/
	writel(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);

	/* 打开中断：Enable group0 and group1 interrupt forwarding.*/
	writel(GICD_ENABLE, base + GIC_DIST_CTRL);
}

void gic_cpu_init(struct gic_chip_data *gic){
    int i;
    unsigned long base = gic_cpu_base(gic);
    unsigned long dist_base = gic_dist_base(gic);

	for (i = 0; i < 32; i += 4)
		writel(0xa0a0a0a0,
			dist_base + GIC_DIST_PRI + i * 4 / 4);

	writel(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);

	writel(GICC_ENABLE, base + GIC_CPU_CTRL);
}


static void check_virtualization_support(struct gic_chip_data *gic)
{
    unsigned long dist_base = gic_dist_base(gic);
    unsigned long cpu_base = gic_cpu_base(gic);
    
    uint32_t gicd_typer = readl(dist_base + 0x0004);
    uint32_t gicc_ctlr = readl(cpu_base + 0x0000);
    
    printk("=== Virtualization Support Check ===\n");
    
    // 检查GICD_TYPER中的虚拟化支持位
    if (gicd_typer & (1 << 26)) {
        printk("✓ GICv2 Virtualization Extensions supported\n");
        
        // 读取GIC虚拟控制器寄存器
        uint32_t gicd_ctlr = readl(dist_base + 0x0000);
        printk("GICD_CTLR: 0x%08x\n", gicd_ctlr);
        
        // 检查是否启用了虚拟化
        if (gicd_ctlr & (1 << 4)) {
            printk("✓ Virtualization extensions enabled\n");
        } else {
            printk("✗ Virtualization extensions disabled\n");
        }
        
    } else {
        printk("✗ GICv2 Virtualization Extensions NOT supported\n");
    }
    
    // 检查CPU接口的虚拟化支持
    if (gicc_ctlr & (1 << 5)) {
        printk("✓ CPU interface supports virtualization\n");
    } else {
        printk("✗ CPU interface does NOT support virtualization\n");
    }
}

static void check_gic_version(struct gic_chip_data *gic)
{
    uint32_t gicd_typer;
    
    // 读取GICD_TYPER寄存器（偏移0x0004）
    gicd_typer = readl(gic_dist_base(gic) + 0x0004);
    
    printk("GICD_TYPER: 0x%08x\n", gicd_typer);
    
    // 提取版本信息
    if (gicd_typer & (1 << 25)) {
        printk("GICv4 detected\n");
    } else if (gicd_typer & (1 << 24)) {
        printk("GICv3 detected\n");
    } else {
        printk("GICv2 detected\n");
    }
}

int gic_init(int chip, unsigned long dist_base, unsigned long cpu_base){
    
    struct gic_chip_data *gic;
    int gic_irqs;
    int virq_base;
    // cpu core id
    gic = &gic_data[chip];
    // core -> gic  
    gic->raw_cpu_base = cpu_base;
    gic->raw_dist_base = dist_base;

    // interrupt num (max = 1020)
    gic_irqs = readl(gic_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
    gic_irqs = (gic_irqs + 1) * 32;
    if (gic_irqs > 1020)
        gic_irqs = 1020;

    gic->gic_irqs = gic_irqs;
    
    check_gic_version(gic);
    check_virtualization_support(gic);

    printk("%s: cpu_base:0x%x, dist_base:0x%x, gic_irqs:%d\n",
            __func__,cpu_base, dist_base, gic->gic_irqs);
    
    gic_dist_init(gic);
    gic_cpu_init(gic);

    return 0;
    
}